package juc;

import org.junit.Test;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author wuguojian
 * @description
 * @date 2022/11/22 下午6:18
 **/
public class CLHLock {

    private static class CLHNode {
        // 锁状态：默认为false，表示线程没有获取到锁；true表示线程获取到锁或正在等待
        // 为了保证locked状态是线程间可见的，因此用volatile关键字修饰
        volatile boolean locked;
        CLHNode preNode;
        public static final CLHNode EMPTY_NODE = new CLHNode(false, null);

        public CLHNode(boolean locked, CLHNode prevNode) {
            this.locked = locked;
            this.preNode = prevNode;
        }
    }

    /**
     * 【注意】这里用了java的原子系列之AtomicReference，能保证原子更新
      */
    private final AtomicReference<CLHNode> tailNode = new AtomicReference<>();

    private final ThreadLocal<CLHNode> curNodeLocal = new ThreadLocal<>();

    public CLHLock() {
        /**
         * 初始化时尾结点指向一个空的CLH节点
          */
        tailNode.getAndSet(CLHNode.EMPTY_NODE);
    }

    /**
     * 获取锁
     */
    public void lock() {
        // 取出当前线程ThreadLocal存储的当前节点，初始化值总是一个新建的CLHNode，locked状态为false。
        CLHNode curNode = new CLHNode(true, null);
        // 底层就是compareAndSwapObject
        CLHNode preNode = tailNode.getAndSet(curNode);
        curNode.preNode =  preNode;
        while (preNode.locked) {
            Thread.yield();
        }
        // 能执行到这里，说明当前线程获取到了锁
        curNodeLocal.set(curNode);
    }

    /**
     * 释放锁
     */
    public void unlock() {
        CLHNode node = curNodeLocal.get();
        node.locked = false;
        node.preNode=null;
        curNodeLocal.set(null);
    }


    public static int sum = 0;

    @Test
    public void test() {
        // 速度对比
        // ReentrantLock 1 000 000 次 0.154 秒
        // CLHLock 1 000 000 次 2.798 秒
        // 每条线程的执行轮数
        final int TURNS = 100000;
        // 线程数
        final int THREADS = 10;
        //线程池，用于多线程模拟测试
        ExecutorService pool = Executors.newFixedThreadPool(THREADS);
        CLHLock lock = new CLHLock();
        // ReentrantLock 效率高很多
//        Lock lock = new ReentrantLock();
        // 倒数闩
        CountDownLatch countDownLatch = new CountDownLatch(THREADS);
        long start = System.currentTimeMillis();
        for (int i = 0; i < THREADS; i++) {
            pool.submit(() -> {
                for (int j = 0; j < TURNS; j++) {
                    lock.lock();
                    try{
                        sum++;
                    }finally {
                        lock.unlock();
                    }
                }
                System.out.println("本线程累加完成");
                //倒数闩减少 1 次
                countDownLatch.countDown();
            });
        }
        try {
            //等待倒数闩归 0，所有线程结束
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        float time = (System.currentTimeMillis() - start) / 1000F;

        System.out.println("运行的时长为：" + time);
        System.out.println("累加结果为：" + sum);
    }



}
